﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

using ProjectBase.Core;

using Survey.Common;
using Survey.Common.Validation;

namespace Survey.Business
{
    public class StringLengthRule<TItem> : RuleBase, IValidationRule<TItem>
        where TItem : class
    {
        #region members
        private readonly Func<TItem, string> _compiledExpression;
        string _propertyName;
        #endregion members

        #region Constructor
        public StringLengthRule(Expression<Func<TItem, string>> expression)
        {
            Contract.Requires(expression.Is());

            var body = expression.Body as MemberExpression;
            if (body.IsNull())
            {
                throw new NotSupportedException(
                    "Provided 'Expression<Func<TITem, string>>' must be of a 'ExpressionType.MemberAccess' (StringLengthRule). ");
            }

            _propertyName = body.Member.Name;
            _compiledExpression = expression.Compile();
        }
        public StringLengthRule(Func<TItem, string> expression, string propertyName)
        {
            Contract.Requires(expression.Is());

            _propertyName = propertyName;
            _compiledExpression = expression;
        }
        #endregion Constructor

        #region Validate

        public virtual ValidationResult Validate(TItem item, out ValidationMessage message)
        {
            message = null;

            // item cannot be null
            if (item.IsNull())
            {
                message = new ValidationMessage(Str.Messages.NullInsteadOfEntityProvided1)
                {
                    EntityName,
                };
                return ValidationResult.Error;
            }

            var result = CompiledExpression.Invoke(item);
            var entity = item as IPersistentObject;
            var itemId = entity.IsNull()
                ? "N/A"
                : entity.IsTransient() ? "new" : entity.IDToDisplay;

            // NOT Required string could be null or empty
            if (result.IsEmpty() && !MinLength.HasValue)
            {
                return ValidationResult.IsValid;
            }

            // property is NULL
            if (result.IsNull())
            {
                if (!MinLength.HasValue)
                {
                    return ValidationResult.IsValid;
                }

                message = new ValidationMessage(Str.Messages.MissingProperty2)
                {
                    itemId,
                    PropertyName.GetLocalized(EntityName),
                };
                return ValidationResult.Error;
            }

            // is too short
            var isShorter = MinLength.HasValue
                && MinLength.Value > result.Length;
            if (isShorter)
            {
                message = new ValidationMessage(Str.Messages.LengthTooShort4)
                {
                    PropertyName.GetLocalized(EntityName),
                    itemId,
                    result.Length,
                    MinLength.Value,
                };
                return ValidationResult.Error;
            }

            // is too long
            var isLonger = MaxLength < result.Length;
            if (isLonger)
            {
                message = new ValidationMessage(Str.Messages.LengthTooLong4)
                {
                    PropertyName.GetLocalized(EntityName),
                    itemId,
                    result.Length,
                    MaxLength,
                };
                return ValidationResult.Error;
            }

            return ValidationResult.IsValid;
        }

        #endregion Validate

        #region fluent

        public virtual StringLengthRule<TItem> AsRequired()
        {
            MinLength = MinLength ?? 0;
            return this;
        }

        public virtual StringLengthRule<TItem> AllowNull()
        {
            MinLength = null;
            return this;
        }

        public virtual StringLengthRule<TItem> LimitLengthTo(int upper)
        {
            MaxLength = upper;
            return this;
        }

        public virtual StringLengthRule<TItem> RequireLengthBetween(int lower, int upper)
        {
            MinLength = lower;
            MaxLength = upper;
            return this;
        }

        #endregion fluent

        #region Client

        public override string GetClientRule(string prefix = null)
        {
            var isRequired = MinLength.HasValue && MinLength.Value > 0;
            var rule = Templates.String.FormatWith(prefix.Extend(PropertyName),
                    isRequired.ToString().ToLowerInvariant(),
                    (MinLength ?? 0).ToString(CultureInfo.InvariantCulture),
                    MaxLength.ToString(CultureInfo.InvariantCulture))
                ;
            return rule;
        }

        #endregion Client

        #region properties
        protected virtual Func<TItem, string> CompiledExpression
        {
            get { return _compiledExpression; }
        }
        protected virtual int? MinLength { get; set; }
        protected virtual int MaxLength { get; set; }
        protected virtual string PropertyName
        {
            get { return _propertyName; }
            set { _propertyName = value; }
        }
        protected virtual string EntityName { get { return typeof(TItem).Name; } }
        #endregion properties
    }
}